第五章 页面布局

发表于 2018-1-14 17:02:43 | 分类于 CSS |

布局的基本概念

多栏布局有三种基本的实现方案:固定宽度、流动、弹性。

  • 固定宽度

    • 固定宽度布局的大小不会随用户调整浏览器窗口大小而变化。
    • 一般是 900 到 1100像素宽。其中 960 像素是最常见的,因为这个宽度适合所有现代显示器,而且能够被 16、12、10、8、6、5、4 和 3 整除,不仅容易计算等宽分栏的数量,而且计算结果也能得到没有小数的像素数。
  • 流动布局

    • 流动的大小会随用户调整浏览器窗口大小而变化。
    • 这种布局能够更好地适应大屏幕,但同时也意味着放弃对页面某些方面的控制,比如随着页面宽度变化,文本行的长度和页面元素之间的位置关系都可能变化。
    • 流动中栏布局,在各栏宽度加大时通过为内容元素周围添加空白来保持内容居中,而且现在的导航条会在布局变窄到某个宽度时收缩进一个下拉菜单中,从而为内容腾出空间。
    • CSS 媒体查询,这就让基于浏览器窗口宽度提供不同的 CSS 样式成为可能。这种可变的固定布局能够适应最大和最小的屏幕,业界称之为响应式设计。
  • 弹性布局与流动布局类似,在浏览器窗口变宽时,不仅布局变宽,而且所有内容元素的大小也会变化,让人产生一种所有东西都变大了的感觉。

布局高度与布局宽度

布局高度

多数情况下,布局中结构化元素(乃至任何元素)的高度是不必设定的。甚至想根本不应该给元素设定高度。除非确实需要这样做,比如在页面中创造一个绝对定位的元素。

正常情况下都应该保持元素 height 属性的默认值 auto 不变,这是因为只有这样,元素才能随自己包含内容的增加而在垂直方向上扩展。这样扩展的元素会把下方的元素向下推,而布局也能随着内容数量的增减而垂直伸缩。

假如你明确设定了元素的高度,那么超出的内容要么被剪掉,要么会跑到容器之外——取决于元素 overflow 属性的设定。

布局宽度

与高度不同,需要更精细地控制布局宽度,以便随着浏览器窗口宽度的合理变化,布局能够作出适当的调整,确保文本行不会过长或过短。

如果随意给元素添加内边距、边框,或者元素本身过大,导致浮动元素的宽度超过包含元素的布局宽度,那浮动元素就可能“躲”到其他元素下方。

即使必须设定栏宽,也不要给包含在其中的内容元素设定宽度,应该让这些内容元素自动扩展到填满栏的宽度。这是块级元素的默认行为。

简言之,就是让栏宽限制其中内容元素的宽度。

三栏 - 固定宽度布局

只要掌握了创建三栏布局的技术,那么,想创建多少栏就能创建多少栏。

<div id="wrapper">
    <article>
        <! -- 这里是一些文本元素 -->
    </article>
</div>
#wrapper {
    width:960px; 
    margin:0 auto; 
    border:1px solid;
}

article {background:#ffed53;}

通过给整个外包装设定宽度值,并将其水平外边距设定为 auto,这个单栏布局在页面上居中了。随着向里添加内容,这一栏的高度会相应增加。

外包装中的 article 元素本质上就是一个没有宽度的块级盒子,它水平扩展填满了外包装。

下面,再向外包装里添加一个导航元素,让它作为第二栏。

<div id="wrapper">
    <nav>
        <!-- 无序列表 -->
    </nav>
    <article>
        <!-- 文本  -->
    </article>
</div>

浮动作为两栏的容器元素,让它们并排显示:

#wrapper {
    width:960px; 
    margin:0 auto; 
    border:1px solid;
}

nav {
    width:150px;
    float:left;
}

nav li {
    /*去掉列表项目符号*/
    list-style-type:none;
}

article {
    width:810px;
    float:left;
    background:#ffed53;
}

把两栏容器元素的总宽度设定为外包装的宽度(150 + 810 = 960),并浮动它们,就可以创造出并肩排列的两栏来。每一栏的长度取决于内容多少。采用同样的方法,可以添加第三栏(或任意多个栏)。

<div id="wrapper">
    <nav>
        <!-- 无序列表 -->
    </nav>
    <article>
         <!-- 文本  -->
    </article>
    <aside>
        <!-- 文本 -->
    </aside>
</div>

调整article 这一栏的宽度,为第三栏腾出空间。

#wrapper {
    width:960px; 
    margin:0 auto; 
    border:1px solid;
}

nav {
    width:150px;
    float:left;
    background:#dcd9c0;
}

nav li {
    list-style-type:none;
}

article {
    width:600px;
    float:left;
    background:#ffed53;
}

aside {
    width:210px;
    float:left;
    background:#3f7ccf;
}

通过把三个浮动容器的总宽度设定为恰好等于外包装的宽度(150 + 600 + 210 = 960),就有了三栏布局的框架。用这种办法,可以想加多少栏就加多少栏,只要它们的总宽度等于外包装的宽度即可。

添加与布局同宽的页眉和页脚:

<div id="wrapper">
    <header>
    <   !-- 标题 -->
    </header>
    <nav>
        <!-- 无序列表 -->
    </nav>
    <article>
        <!-- 文本 -->
    </article>
    <aside>
        <!-- 文本 -->
    </aside>
    <footer>
        <!-- 文本 -->
    </footer>
</div>

希望页眉和页脚与布局同宽,而且它们默认就与布局同宽,在此只简单地为它们设定了背景色:

header {background:#f00;}
footer {background:#000;}

此时的页眉与布局同宽,其内容高度也比较合适。但是,页脚位于浮动元素后面,所以就会尽量往上移动。解决方法:

footer {clear:both;}

为页脚应用 clear:bothclear:left 效果也一样,因为这里只有左浮动元素),就可以阻止它向上移动,不让它超过浮动元素的下方边界,就可以保证页脚始终都位于最长栏的下方。

到目前为止,为 HTML 标记应用的 CSS 如下:

* {
    margin:0; 
    padding:0;
}

#wrapper {
    width:960px; 
    margin:0 auto; 
    border:1px solid;
}

header {background:#f00;}

nav {
    width:150px;
    float:left;
    background:#dcd9c0;
}

nav li {
    list-style-type:none;
}

article {
    width:600px;
    float:left;
    background:#ffed53;
}

aside {
    width:210px;
    float:left;
    background:#3f7ccf;
}

footer {
    clear:both; 
    background:#000;
}

为栏设定内边距和边框

只要一调整各栏中的内容,布局就可能超过容器宽度,而右边的栏就可能滑到左边的栏下方。一般来说,两种情况下可能会发生这种问题。

  • 为了让内容与栏边界空开距离,给栏添加水平外边距和内边距,或者为了增加栏间距,为栏添加外边距(只要开始给布局添加样式,就一定会采用这里说的一种做法,甚至双管齐下),导致布局宽度增大,进而浮动栏下滑。换句话说,右边浮动的栏因为没有足够的空间与其他栏并列,就会滑到左边栏的下方。
  • 在栏中添加大图片,或者没有空格的长字符串(如长 URL),也会导致栏宽超过布局宽度。同样,这种情况下右边的栏也会滑到左边的栏下方。

添加内边距,增大内容与栏边界的距离:

article {
    width:600px;
    float:left;
    background:#ffed53;
    padding:10px 20px;
}

中间栏的文本四周都增加了间距,与栏边界保持了一定距离。可是,这样一来由于中间栏的总宽度增加,导致右边的栏不能再与前两栏并排在一起,结 果就跑到了左边栏的下方。这是因为,给固定宽度的元素添加水平外边距、边框和内边距,会导致元素盒子变宽。

有三种方法来预防该问题发生:

  • 从设定的元素宽度中减去添加的水平外边距、边框和内边距的宽度和。
  • 在容器内部的元素上添加内边距或外边距。
  • 使用 CSS3 的 box-sizing 属性切换盒子缩放方式,比如 section {box-sizing:border-box;} 。 应用 box-sizing 属性后,给 section 添加边框和内边距都不会增大盒子,相反会导致内容变窄。

重设宽度以抵消内边距和边框

比如给 600 像素宽的栏又添加了 20 像素的内边距,为了抵消增加的内边距,可以把栏宽减少 40 像素而设定为 560 像素。这样,右边的栏就能归位。问题在于,每次只要调整内、外边距就要重设布局宽度。因此这个办法虽然可行,却不够理想。

给容器内部的元素应用内边距和边框

把外边距和内边距应用到内容元素上确实奏效。前提是这些元素没有明确地设定宽度,这样它们的内容才会随着内、外边距的增加而缩小。就像盒模型定义所说的,没有宽度的元素在水平方向上会适应其父元素,其内容会随着外边距、边框和内边距的增加而减少。

然而,一栏之中可能会包含大量不同内容的元素。假如将来又决定调整内容与容器边界的距离,就必须每个元素都要进行调整,这样不仅麻烦,而且容易出错。况且,给栏添加边框同样会增大栏宽,不可能通过为其包含的内容元素逐个应用样式来做到。

与其为容器中的元素添加外边距,不如在栏中再添加一个没有宽度的 div, 让它包含所有内容元素,然后再给这个 div 应用边框和内边距。如此一来,只要为内部 div 设定一次样式,就可以把让所有内容元素与栏边界保持一致的距离。而且,将来再需要调整时也会很方便。任何新增内容元素的宽度都由这个内部 div 决定。

<article>
    <div class="inner">
        <!-- 文本 -->
    </div>
</article>
article {
    width:600px;
    float:left;
    padding:10px 20px;
    background:#ffed53;
}

article .inner {
    margin:10px;
    border:2px solid red;
    padding:20px;
}

中间栏的宽度并未因此有什么变化,因为内容区减少的宽度抵消了应用到内部 div 上的外边距、边框和内边距的总宽度。总之,由此可以得出一个结论:如果布局中的栏是浮动的,而且都设定了宽度,就不要去动它。要动,就把内容放在内部 div 里,动这个 div。

接下来,去掉中间栏的外边距、边框和内边距,给其他两栏也添加内部 div,然后只给这三栏加上内边距。

<div id="wrapper">
    <header>
        <!-- header text -->
    </header>
    <nav>
        <div class="inner">
            <ul>
                <!-- 链接 -->
            </ul>
        </div>
    </nav>
    <article>
        <div class="inner">
            <!-- 文本 -->
        </div>
    </article>
    <aside>
        <div class="inner">
            <!-- 文本 -->
        </div>
    </aside>
    <footer>
        <!-- 文本 -->
    </footer>
</div>

利用这个 div 为三个栏中的内容创造间距。此外,还居中了页脚中的 内容。

nav .inner {padding:10px;}
article .inner {padding:10px 20px;}
aside .inner {padding:10px;}
footer {text-align:center}

处理栏及其内部 div 的关键在于,浮动栏并设定栏宽,但不给任何内容元素设定宽度。要让内容元素扩展以填充它们的父元素——内部 div。这样,只要简单地设定内部 div 的外边距和内边距,就可以让它们以及它们包含的内容与栏边界保持一定距离。

如果容器的上、下边框不可见,内部 div 的上、下外边距会叠加。要是遇到了这个问题,可以只为容器设定垂直内边距。但要小心一点,别一块儿也添加水平内边距,比如 article {padding:20px 0;},就只设定了上、下内边距。

子-星选择符

“子-星选择符” 是一个组合选择符,利用它可以不使用内部 div 就能设定一栏中所有元素的外边距。

星号选择符可以选择 “所有元素”,故而,在一个选择符后面加个星号,比如 someSelector * 就可以选择 someSelector 所代表元素的所有后代元素。

子选择符可以选择 “某元素的子元素”,故而,把子选择符放到星号前面,比如 someSelector > * 就会只选择 someSelector 所代表元素的所有子元素,而非后代元素。这正好适用于选择容器内部的所有顶部元素,然后设定它们的外边距。

比如,对于 section 栏,设定 section > * {margin:0 10px;},就 能为栏中所有子元素,不包括其他后代元素,各应用 10 像素的左、右外边距。

使用 “子-星选择符” 要注意两点:

  • 第一,在为子元素设定垂直外边距时,只能使用 margin-top 和 margin-bottom,不能使用简写的 margin,否则会抵消用 “子-星选择符” 应用给这些元素的水平外边距。如果你想进一步缩进某个子元素的内容,就应该给该子元素应用内边距。
  • 第二,“子-星选择符” 有潜在性能问题,因为它会导致浏览器遍历整个 DOM 结构去查找所有匹配的元素。但这一点性能影响几乎可以忽略不计。假如你的页面真的包含几千上万个元素,那确实该考虑用性能度量工具测一测这个选择符的影响。

使用 box-sizing:border-box

只要在三个浮动的栏的 CSS 规则中分别加上 box-sizing:border-box 声明,再给栏添加内边距(和边框)就不会导致盒子的宽度变化了。

此时,既不用调整栏宽去抵消增加的内边距,也不用使用内部 div。添加内 边距的结果就是内容收缩。

<div id="wrapper">
    <header>
        <!-- 标题 -->
    </header>
    <nav>
        <ul>
            <!-- 链接 -->
        </ul>
    </nav>
    <article>
        <!-- 文本 -->
    </article>
    <aside>
        <!-- 文本 -->
    </aside>
    <footer>
        <!-- 文本 -->
    </footer>
</div>
* {
    margin:0; 
    padding:0;
}

#wrapper {
    width:960px; 
    margin:0 auto; 
    border:1px solid #000;
    overflow:hidden;
}

header {background:#f00;}

nav {
    box-sizing:border-box;
    width:150px;
    float:left;
    background:#dcd9c0;
    padding:10px 10px;
}

/*去掉列表项目符号*/
nav li {list-style-type:none;}

article {
    box-sizing:border-box;
    width:600px;
    float:left;
    background:#ffed53;
    padding:10px 20px;
}

aside {
    box-sizing:border-box;
    width:210px;
    float:left;
    background:#3f7ccf;
    padding:10px 10px;
}

footer {
    clear:both; 
    background:#000;
}

直接给栏应用内边距会导致内容变窄,但不会影响布局。但是,IE6 和 IE7 不支持 box-sizing 属性。不过,有一个专门解决这个问题的腻子脚本(polyfill),名叫 borderBoxModel.js 。

可以使用条件注释(以便只有 IE6 和 IE7 加载)把它添加到 HTML 标记之后、结束的 `</body> 标签之前,以保证在加载 DOM 之后再执行该脚本:

<body>
    <!-- HTML 标记 -->
    <!-- 只让IE8 之前的IE 加载它 -->
    <!--[if lt IE 8 ]>
    <script src="helpers/borderBoxModel.js"></script>
    <![endif]-->
</body>

这样,IE6 和 IE7 就可以根据 box-sizing 属性的设定正确地调整栏的大小了。

不仅给浮动的栏,甚至给所有元素都应用这个不同的盒缩放模型都是没有问题的,在 CSS 里会添加这一条规则:

* {box-sizing:border-box}

如此一来,每个盒子的宽度并不是内容区的宽度,而是一经设定就不可变的真正的盒子宽度。

最新版本的 borderBoxModel.js 腻子脚本以及它的用途和局限性,可以参考这里: https://github.com/albertogasparin/borderBoxModel。

预防过大的元素

设计一个将来可能由他人维护的动态网站时,需要考虑得更长远一些。比如,应该预见到可能出现一些过大的元素。

如果将来有一张比浮动栏更宽的图片被放到栏中,就会导致布局变宽, 而右边的栏又会滑到下方。

为此, 一个简单的预防措施就是添加一条 .inner img {max-width:100%;} 声明,以便限制图片的宽度不超过其父元素(在此就是内部 div)。

另一个办法是给每个栏(或者内部 div,如果用了的话)添加 overflow:hidden 声明。这条声明不会缩小图片以适应父元素,而会将它(以及任何过大元素)超出容器边界的部分剪切掉。

动态网站中另一个潜在的问题是换行。HTML 只会在单词间空格的地方换行。一些长 URL,甚至一些长单词,在栏比较窄的情况下,都会导致栏宽过大。

因此,还应该给所有栏的外包装元素应用 word-wrap:break-word 声明,以便所有栏及其内容继承这个设定。有了这条声明,浏览器会把过长的词断开显示在不同行上。

只是 word-wrap 没有定义在哪里断开,因此结果完全是随机的,而且没有连字符。不过,这条规则只在需要时才会起作用,而且能保护布局不会被 长 URL 顶得支离破碎。建议在每一栏中都用长 URL、大图片,以及包含内容过多的元素测试一下布局,看看这些声明到底会不会起作用,并发现更多需要事先考虑保护措施的其他漏洞。

三栏 - 中栏流动布局

实现中栏流动布局有两种方法。:

  • 在中栏改变大小时使用负外边距定位右栏。
  • 使用 CSS3 让栏容器具有类似表格单元的行为。

负外边距适合比较老的浏览器,而 CSS 的 table 属性则要简单得多。

用负外边距实现

实现三栏布局且让中栏内容区流动(不固定)的核心问题,就是处理右栏的定位,并在中栏内容区大小改变时控制右栏与布局的关系。

解决方案是控制两个外包装容器的外边距。其中一个外包装包围所有三栏,另一个外包装只包围左栏和中栏。

<div id="main_wrapper">
    <header>
        <!-- 页眉-->
    </header>
    <div id="threecolwrap">/*三栏外包装(包围全部三栏)*/
        <div id="twocolwrap">/*两栏外包装(包围左栏和中栏)*/
            /*左栏*/
            <nav>
                <!-- 导航 -->
            </nav>
            /*中栏*/
            <article>
             <!-- 区块 -->
            </article>
        </div>/*结束两栏外包装(twocolwrap)*/
        /*右栏*/
        <aside>
            <!-- 侧栏 -->
        </aside>
    </div>/*结束三栏外包装(threecolwrap)*/
    <footer>
        <!-- 页脚 -->
    </footer>
</div>
* {
    margin:0; 
    padding:0;
}

body {font:1em helvetica, arial, sans-serif;}

div#main_wrapper {
    min-width:600px; 
    max-width:1100px;

    /*超过最大宽度时,居中布局*/
    margin:0 auto;

    /*背景图片默认从左上角开始拼接*/
    background:url(images/bg_tile_150pxw.png) repeat-y #eee;
}

header {
    padding:5px 10px;
    background:#3f7ccf;
}

div#threecolwrap {
    /*浮动强制它包围浮动的栏*/
    float:left;
    width:100%;

    /*背景图片右对齐*/
    background:url(images/bg_tile_210pxw.png) top right repeat-y;
}

div#twocolwrap {
    /*浮动强制它包围浮动的栏*/
    float:left;
    width:100%;

    /*把右栏拉到区块外边距腾出的位置上*/
    margin-right:-210px;
}

nav {
    float:left;
    width:150px;
    background:#f00;
    padding:20px 0;
}

/*让子元素与栏边界保持一定距离*/
nav > * {margin:0 10px;}

article {
    width:auto;
    margin-left:150px;
 
    /*在流动居中的栏右侧腾出空间*/
    margin-right:210px;
 
    background:#eee;
    padding:20px 0;
}

/*让子元素与栏边界保持一定距离*/
article > * {margin:0 20px;}

aside {
    float:left;
    width:210px;
    background:#ffed53;
    padding:20px 0;
}

/*让子元素与栏边界保持一定距离*/
aside > * {margin:0 10px;}

footer {
    clear:both;
    width:100%;
    text-align:center;
    background:#000;
}

三栏中的右栏是 210 像素宽。为了给右栏腾出空间,中栏 article 元素有一个 210 像素的右外边距。

仅仅有这个外边距,只能把右栏再向右推 210 像素。

包围左栏和中栏的两栏外包装上 210 像素的负右外边距,会把右栏拉回 article 元素右外边距(在两栏外包装内部右侧)创造的空间内。

中栏 aticle 元素的宽度是 auto,因此它仍然会力求占据浮动左栏剩余的所有空间。可是,一方面它自己的右外边距在两栏外包装内为右栏腾出了空间,另一方面两栏外包装的负右外边距又把右栏拉到了该空间内。

人造栏技术

这些栏看起来都跟布局一样高,其实都是假象。

这里采用了一种叫 “人造栏” 的技术,这样才让所有栏看起来都一样高了。

该技术就是给包围栏的外包装元素应用与各栏同宽的背景图片和背景色。

外包装元素跟它们包含的栏不一样,它们的高度就是布局高度,当然与内容 区的高度相同。

通过在它们的背景的垂直方向上重复拼接背景图片,就可以在视觉上造成各栏与布局同高的假象。

这个例子分别为左栏和右栏应用了不同的背景图片,为流动的中栏应用了背景色。

左栏的背景图片加在了 div#main_wrapper 上。右栏的背景图片加在了 div#threecolwrap 上,而且让它沿该 div 的右侧垂直拼接。

中栏的背景色也加在了 div#main_wrapper 上,这个背景色实际上是整个布局的背景色。

而位于两侧的两栏的背景图片,以及页眉和页脚的背景色,都会覆盖这个背景色(子元素覆盖父元素)。因此,只能在中栏看到该全局背景色。

用 CSS3 单元格实现

利用 HTML 的 <table> 标签实现多栏布局是难以接受的,但使用 CSS 让布局形如表格则是可以接受的。这种方法不会导致固定不变的表格布局,也不会出现难以重新应用样式的问题(比如在手持设备上表现为一栏)。

CSS 可以把一个 HTML 元素的 display 属性设定为 table、table-row 和 table-cell。通过这种方法可以模拟相应 HTML 元素的行为。

通过 CSS 把布局中的栏设定为 table-cell 有三个好处:

  • 单元格(table-cell)不需要浮动就可以并排显示,而且直接为它们应用内边距也不会破坏布局。
  • 默认情况下,一行中的所有单元格高度相同,因而也不需要人造的等高栏效果了。
  • 任何没有明确设定宽度的栏都是流动的。

这三个好处解决了本章前面学习布局时遇到的问题。然而,CSS3 表格行为在 IE7 及更低版本中并没有得到支持,而且也没有稳妥的补救措施。如果愿意摒弃IE7,那么它就是一个既简单又可靠,而且还很彻底的解决方案。

甚至都不需要用 div 外包装来扮演 table 和 tr 元素,仅仅是把三栏的 display 属性设定为 table-cell 就可以了。

浏览器的排版引擎在碰到没有表行(tr)的一组单元格时,会自动为它们添加表行,而在表行没有被 table 元素包围时,会自动为表行添加 table。

因此,不需要多写任何标记,只要把每一栏的 display 属性设定为 table-cell,剩下的事儿就可全部交给浏览器负责。

要实现一个三栏流动中栏布局,只需要以下标记:

<nav>
    <!-- 内容 -->
</nav>
<article>
    <!-- 内容 -->
</article>
<aside>
    <!-- 内容 -->
</aside>
nav {
    display:table-cell; 
    width:150px; 
    padding:10px;
    background:#dcd9c0;
}

article { 
    display:table-cell; 
    padding:10px 20px;
    background:#ffed53;
}

aside {
    display:table-cell; 
    width:210px; 
    padding:10px;
    background:#3f7ccf;
}

这个简单、功能完备的布局对 IE7 和 IE6 可没有任何腻子脚本,连退化的后备方案都没有。在这些浏览器中,三栏会上下堆叠在一起。

多行多栏布局

CSS 选择符的实际应用

前面的布局中只包含一个 article、一个 nav……,因此比较容易选择,只要用标签名即可。而在创建复杂布局时,一个页面中会出现多个相同的标签,选择的时候就要用上下文选择符来区分它们了。

随着页面变得越来越复杂,相同的 HTML 元素(如section、article、nav,等等)会出现很多次。为了选择某个元素,必须区分这些相同的标签名。

有些新手会给每个标签都添加一个不同的类名。

但这种做法是不值得提倡的。不仅因为类本身就不该这么用(类应该用于标记具有相同特征的元素),而且这么多类会把标记弄得很乱,让 CSS 也很难看懂。为了知道每个类代表哪个元素,必须不断地查看 HTML。

更好的做法是给标记中每个主要区域的顶级元素添加一个 ID,这也是使用 ID 的正确方式,ID 就是标识页面中唯一元素用的。

然后,这些 ID 就会成为 HTML 标记中的 “路标”,放在上下文选择符开头的时候,它们就能起到框定后代元素的作用。

这就是在标记中保持类和 ID 属性最少的秘诀。而且,相应的上下文选择符也能清晰地传达出路径信息,让人从 CSS 中一眼就能看出它要选择哪个元素。

前面的 HTML 标记。其中的三个 ID 是精确选择任意元素唯一必要的几个 “路标”,即使再给布局中添加一些内容元素,也用不着更多的 “路标”。

只要顶级元素上有一个 ID,就可以把它作为 “路标”,进而选择它的任意 后代元素(甚至能够选择将来才会加入其中的内容元素)。另外,同样重要的是,这样也避免了无意中把样式添加给标记中的其他元素上。

如果在没有别的手段选择某个特定元素的情况下,只在标记中添加一个类或一个 ID,那么 HTML 就能保持清晰整洁,而 CSS 也将易读易维护。

内部 DIV 实战

每一栏中的间距要依靠内部 div。水平间距是由内部 div 的左、右外边距生成的,它们把这个 div 压缩了一下,这才使内容远离了父元素 (如果此时直接给父元素应用水平内边距的话,一定会破坏布局)。

而每一栏中的垂直间距是由父元素的内边距生成的。这是因为,就是在父元素没有上、下边框的情况下,子元素的上、下外边距会折叠的。